Skip to content

feat(litestar): default prometheus group_path=True to bound cardinality#144

Merged
lesnik512 merged 1 commit into
mainfrom
litestar-prometheus-group-path-default
Jul 1, 2026
Merged

feat(litestar): default prometheus group_path=True to bound cardinality#144
lesnik512 merged 1 commit into
mainfrom
litestar-prometheus-group-path-default

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Problem

Litestar's PrometheusConfig defaults group_path=False, so the path metric label records the raw URL. Any route with path parameters then mints one time series per distinct value, growing the registry without bound. In production this shows up as steadily climbing memory / OOM on the scraping Prometheus. It hits users silently because it is the default.

Reproduced (isolated processes so the global prometheus_client registry doesn't leak series between runs), 5 requests to one parameterized route:

group_path=False: 5 distinct app path series -> ['/users/1', '/users/2', '/users/3', '/users/4', '/users/5']
group_path=True:  1 distinct app path series -> ['/users/{user_id}']

Change

  • New LitestarConfig.prometheus_group_path: bool = True — the path label uses the route template (/users/{id}), bounding cardinality.
  • LitestarPrometheusInstrument.bootstrap merges {"group_path": <field>, **prometheus_additional_params}. Precedence: prometheus_additional_params["group_path"] > prometheus_group_path > Litestar's own False. The merge (not a plain group_path= kwarg) avoids a "multiple values for keyword argument" crash for anyone already passing group_path via the dict — the documented workaround keeps working.
  • FastAPI is untouched: prometheus_fastapi_instrumentator already labels by route template.

Compatibility

Behavior-changing default: the path label now holds the route template instead of the raw URL. Dashboards keyed on raw paths will shift. Revert per-app with prometheus_group_path=False.

Tests

TDD (red before green), unique route prefixes per test to avoid global-registry contamination:

  • default -> path label is the route template, raw path absent
  • prometheus_group_path=False -> raw path recorded
  • prometheus_additional_params={"group_path": False} overrides the True default

just lint-ci clean (ruff + ty + planning validator); full suite 204 passed, 100% coverage.

Docs

  • architecture/instruments.md — new "Prometheus path-label cardinality (Litestar)" subsection.
  • docs/integrations/litestar.md — user-facing Prometheus section.

Upstream

Filed litestar-org/litestar#4891 proposing a warning (2.x) and default flip (next major). This PR gives lite-bootstrap users the safe default now, regardless of upstream.

🤖 Generated with Claude Code

Litestar's PrometheusConfig defaults group_path=False, so the `path`
metric label records the raw URL. Parameterized routes then mint one
series per distinct value and grow the registry unbounded, causing
memory growth (see litestar-org/litestar#4891).

Add LitestarConfig.prometheus_group_path (default True) so the label
uses the route template. The instrument merges
{"group_path": <field>, **prometheus_additional_params} so the dict
still overrides the field without a kwarg collision, keeping the
existing group_path-via-additional_params workaround working.

FastAPI is unaffected: prometheus_fastapi_instrumentator already
labels by route template.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lesnik512 lesnik512 merged commit b1617e2 into main Jul 1, 2026
7 checks passed
@lesnik512 lesnik512 deleted the litestar-prometheus-group-path-default branch July 1, 2026 18:25
lesnik512 added a commit that referenced this pull request Jul 1, 2026
Notes for the Litestar Prometheus group_path=True default (PR #144).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant